// 导入TensorFlow.js库，这是整个深度学习功能的核心
import * as tf from '@tensorflow/tfjs';
// 导入工具函数，用于将文件转换为图像对象
import { file2img } from './utils.js'
// 导入ImageNet类别标签，包含了1000个常见物体的名称
import { IMAGENET_CLASSES } from './imagenet_classes.js'

// MobileNet模型的本地路径
// 注意：这个路径是本地开发服务器的路径，实际使用时需要确保服务器已启动
const MOBILENET_MODEL_PATH = 'http://127.0.0.1:8081/mobilenet/web_model1/model.json'

// 页面加载完成后执行的异步函数
// 使用async/await语法来处理异步操作，使代码更简洁易读
window.onload = async () => {
    // 加载预训练的MobileNet模型
    // MobileNet是一个轻量级的卷积神经网络，专为移动设备和边缘设备设计
    // 它在保持较高准确率的同时，计算复杂度较低，非常适合在浏览器中运行
    const model = await tf.loadLayersModel(MOBILENET_MODEL_PATH);
    console.log('MobileNet模型加载完成，可以开始预测了！');
    
    // 将预测函数挂载到window对象上，使其可以在全局范围内被调用
    // 这个函数接收一个文件对象（通常是用户通过文件选择器上传的图片）
    window.predict = async (file) => {
        // 将文件对象转换为img元素，这一步在utils.js中实现
        // 转换过程会确保图像被调整到MobileNet需要的224x224像素大小
        const img = await file2img(file)
        
        // 使用tf.tidy()包装计算过程，这是TensorFlow.js的内存管理机制
        // 它会自动清理所有在函数内部创建但未返回的中间张量，防止内存泄漏
        const pred = tf.tidy(() => {
            // 图像预处理链式操作，将原始图像转换为模型需要的格式
            const input = tf.browser.fromPixels(img)  // 从图像获取像素数据
                .toFloat()                          // 转换为浮点数
                .sub(255 / 2)                       // 中心化，减去均值
                .div(255 / 2)                       // 归一化到[-1, 1]范围
                .reshape([1, 224, 224, 3])          // 调整形状为[样本数, 高度, 宽度, 通道数]
            
            // 使用加载好的模型进行预测，返回形状为[1, 1000]的张量
            // 这个张量包含了每个ImageNet类别的预测概率
            return model.predict(input)
        })
        
        // 找到概率最大的类别索引
        // argMax(1)会在第1维（类别维度）上找到最大值的索引
        // dataSync()将Tensor转换为JavaScript数组，[0]获取第一个（也是唯一一个）预测结果
        const index = pred.argMax(1).dataSync()[0]
        
        // 使用alert弹窗显示预测结果
        // IMAGENET_CLASSES[index]会根据索引查找对应的类别名称
        alert(`预测结果：${IMAGENET_CLASSES[index]}`)
    }
}